// 从根节点开始渲染 调度
import {
  ELEMENT_TEXT,
  TAG_ROOT,
  TAG_TEXT,
  TAG_HOST,
  PLACEMENT,
  DELETION,
  UPDATE,
  TAG_CLASS,
  TAG_FUNCTION_COMPONENT,
} from "./constants";
import { setProps } from "./utils";
import { UpdateQueue, Update } from "./updateQueue";
// 下一个工作单元
let nextUnitOfWork = null;
let workInProgressRoot = null; // rootFiber 应用的根
let currentRoot = null;
let deletions = [];

// 实现hooks
let workInProgressFiber = null;
let hookIndex = 0;
/**
 * 给 workInProgressRoot 赋值, 开始重新构建fiber树
 */
export function scheduleRoot(fiber) {
  // 第三次渲染
  if (currentRoot && currentRoot.alternate) {  // 第二次之后的更新 -  双缓存机制
    workInProgressRoot = currentRoot.alternate; //第一次渲染处理的fiber tree
    workInProgressRoot.alternate = currentRoot; // 让这个渲染树的替身指向当前的currentRoot
    if (fiber) workInProgressRoot.props = fiber.props; //更新props
  }
  // 第二次渲染 
  else if (currentRoot) { //这里是第一次更新
    if (fiber) {
      //currentRoot存在，说明至少已经渲染一次
      fiber.alternate = currentRoot;
      workInProgressRoot = fiber;
    } else {
      workInProgressRoot = {
        ...currentRoot,
        alternate: currentRoot,
      };
    }
  } 
  // 第一次渲染
  else { 
    workInProgressRoot = fiber;
  }

  // fiber: tag, stateNode, props
  workInProgressRoot.firstEffect =
    workInProgressRoot.lastEffect =
    workInProgressRoot.nextEffect =
      null;
  nextUnitOfWork = workInProgressRoot;
}
// 循环执行 work
function workLoop(deadline) {
  let shouldYield = false; //是否要让出时间片 【对浏览器的控制权】
  while (nextUnitOfWork && !shouldYield) {
    nextUnitOfWork = performUnitOfWork(nextUnitOfWork); // 执行完一个任务后
    shouldYield = deadline.timeRemaining() < 1; // 没有剩余时间，要让出控制权
  }
  // 副作用链收集完成后，开始进行提交
  if (!nextUnitOfWork && workInProgressRoot) { //没有剩余任务，进行commit操作
    console.log("render finished");
    // 进行提交阶段
    commitRoot();
  }
  // 再次请求任务调度
  requestIdleCallback(workLoop, { timeout: 500 });
}
function commitRoot() {
  deletions.forEach(commitWork);//执行effect list之前，先把该删除的元素删除
  let currentFiber = workInProgressRoot.firstEffect;
  while (currentFiber) {
    commitWork(currentFiber);
    currentFiber = currentFiber.nextEffect;
  }
  deletions.length = 0;
  currentRoot = workInProgressRoot;
  workInProgressRoot = null;
}
function commitWork(currentFiber) {
  if (!currentFiber) return;
  let returnFiber = currentFiber.return;
  // 现在判断不是dom节点，说明是类组件，类组件的对象要往上层查找
  while (
    returnFiber.tag !== TAG_HOST &&
    returnFiber.tag !== TAG_ROOT &&
    returnFiber.tag !== TAG_TEXT
  ) {
    // 
    returnFiber = returnFiber.return;
  }
  let returnDOM = returnFiber.stateNode;
  if (currentFiber.effectTag === PLACEMENT) { //新增加节点
    let nextFiber = currentFiber;
    // 如果要挂载的节点不是dom，比如类组件fiber，一直向上查找第一个子节点，直到找到dom节点为止
    while (nextFiber.tag !== TAG_HOST && nextFiber.tag !== TAG_TEXT) {
      nextFiber = currentFiber.child;
    }
    // 新增 节点
    if (nextFiber.stateNode) {
      returnDOM.appendChild(nextFiber.stateNode);
    }
  } else if (currentFiber.effectTag === DELETION) {
    // 删除节点
    // returnDOM.removeChild(currentFiber.stateNode);
    return commitDeletion(currentFiber, returnDOM);
  } else if (currentFiber.effectTag === UPDATE) {
    if (currentFiber.type === ELEMENT_TEXT) {
      // currentFiber.alternate 表示的上个fiber对象
      if (currentFiber.alternate.props.text !== currentFiber.props.text)
        currentFiber.stateNode.textContent = currentFiber.props.text;
    } else {
      updateDOM(
        currentFiber.stateNode, //dom节点
        currentFiber.alternate.props, // 旧props属性
        currentFiber.props // 新props属性
      );
    }
  }
  currentFiber.effectTag = null;
}
function commitDeletion(currentFiber, returnDOM) {
  if (currentFiber.tag === TAG_HOST || currentFiber.tag === TAG_TEXT) {
    if (returnDOM.contains(currentFiber.stateNode)) //判断一下要删除的dom节点是否在returnDOM中
      returnDOM.removeChild(currentFiber.stateNode);
  } else {
    commitDeletion(currentFiber.child, returnDOM);
  }
}
/**
 * @description: 
 * @param {*} currentFiber
 */
function performUnitOfWork(currentFiber) {
  beginWork(currentFiber); // 开始工作
  if (currentFiber.child) {
    //如果有child，遍历child节点
    return currentFiber.child;
  }
  // 创建子Fiber 的关系链
  while (currentFiber) {
    // 没有child，说明当前节点完成遍历
    completeUnitOfWork(currentFiber);
    if (currentFiber.sibling) {
      // 查看有没有兄弟节点
      return currentFiber.sibling; //如果有兄弟节点，则返回兄弟节点
    }
    currentFiber = currentFiber.return; //没有兄弟节点，则先返回父节点，然后再查找父节点的兄弟节点
  }
}
/**
 * @description: dom diff节点创建副作用链的过程
 */
// 完成的时候，收集副作用effect list
// 每个Fiber有两个属性，firstEffect指向第一个有副作用的子fiber，lastEffect指向最后一个有副作用的子fiber
// 中间的用nextEffect做成单链表，firstEffect=大儿子.nextEffect 二儿子.nextEffect 三儿子lastEffect
function completeUnitOfWork(currentFiber) {
  // returnFiber 表示父节点fiber
  let returnFiber = currentFiber.return;
  if (returnFiber) {
    // *********** 把自己子节点的effect链挂到父节点上********
    if (!returnFiber.firstEffect) { //如果fiber没firstEffect，则指定firstEffect
      //如果父节点没有firstEffect，比如A1刚开始没firstEffect
      returnFiber.firstEffect = currentFiber.firstEffect; //就把B1的firstEffect赋值给父节点的firstEffect
    }
    if (currentFiber.lastEffect) {
      // 当前节点有lastEffect，比如B1有lastEffect
      if (returnFiber.lastEffect) {
        //A1也有lastEffect，则把自身给挂到链的末尾
        returnFiber.lastEffect.nextEffect = currentFiber.firstEffect;
      }
      // 第一次时，A1只有firstEffect，还没有lastEffect【C2】时，则把B2的lastEffect【C2】挂给A1的lastEffect
      returnFiber.lastEffect = currentFiber.lastEffect;
    }
    /***** 把自己挂到父节点的链上 ********/
    const effectTag = currentFiber.effectTag;
    if (effectTag) { // 说明 有副作用
    /* 建立副作用的链表 */
      if (returnFiber.lastEffect) {
        returnFiber.lastEffect.nextEffect = currentFiber;
      } else {
        returnFiber.firstEffect = currentFiber;
      }
      returnFiber.lastEffect = currentFiber;
    }
  }
}
/**
 * @description: 1:开始创建下层的fiber 2:创建真实DOM
 */
function beginWork(currentFiber) {
  // 如果 currentFiber 是根fiber
  if (currentFiber.tag === TAG_ROOT) {
    updateHostRoot(currentFiber);
  } else if (currentFiber.tag === TAG_TEXT) { //如果 currentFiber是文本
    updateHostText(currentFiber);
  } else if (currentFiber.tag === TAG_HOST) {
    updateHost(currentFiber); // 原生DOM节点
  } else if (currentFiber.tag === TAG_CLASS) { // 处理类组件 fiber
    updateClassComponent(currentFiber);
  } else if (currentFiber.tag === TAG_FUNCTION_COMPONENT) { //处理函数组件
    updateFunctionComponent(currentFiber);
  }
}
// 更新根组件
function updateHostRoot(currentFiber) {
  //更新根rootFiber
  let newChildren = currentFiber.props.children;
  // 调和 子节点
  reconcileChildren(currentFiber, newChildren);
}
// 更新文本
function updateHostText(currentFiber) {
  if (!currentFiber.stateNode) {
    currentFiber.stateNode = createDOM(currentFiber);
  }
}
// 更新原生节点
function updateHost(currentFiber) {
  if (!currentFiber.stateNode) {
    currentFiber.stateNode = createDOM(currentFiber);
  }
  const newChildren = currentFiber.props.children;
  reconcileChildren(currentFiber, newChildren);
}
// 更新类组件
function updateClassComponent(currentFiber) {
  if (!currentFiber.stateNode) { // 类组件的stateNode组件实例不存在，则创建
    // currentFiber.type 是类组件的名称
    currentFiber.stateNode = new currentFiber.type(currentFiber.props);
    currentFiber.stateNode.internalFiber = currentFiber;
    currentFiber.updateQueue = new UpdateQueue();
  }
  // 给类组件实例的state 赋值
  currentFiber.stateNode.state = currentFiber.updateQueue.forceUpdate(
    currentFiber.stateNode.state
  );
  let newElement = currentFiber.stateNode.render();
  const newChildren = [newElement];
  reconcileChildren(currentFiber, newChildren);
}
function updateFunctionComponent(currentFiber) {
  workInProgressFiber = currentFiber;
  hookIndex = 0; // 每个hook对应着不同的索引，所以hook不能放到判断语句中，hook的创建必须放在顶层
  workInProgressFiber.hooks = [];
  const newChildren = [currentFiber.type(currentFiber.props)]; //让函数执行
  reconcileChildren(currentFiber, newChildren);
}
function createDOM(currentFiber) {
  if (currentFiber.tag === TAG_TEXT) {
    return document.createTextNode(currentFiber.props.text);
  } else if (currentFiber.tag === TAG_HOST) { // 如果是原生 html 节点
    let stateNode = document.createElement(currentFiber.type);
    updateDOM(stateNode, {}, currentFiber.props);
    return stateNode;
  }
}
function updateDOM(stateNode, oldProps, newProps) {
  if (stateNode && stateNode.setAttribute) {
    setProps(stateNode, oldProps, newProps);
  }
}
/**
 * @description: 对比fiber 对象，和下层子元素
 * @param {*} currentFiber
 * @param {*} newChildren：虚拟DOM的数组，把虚拟DOM转成Fiber节点
 * @return {*}
 */
function reconcileChildren(currentFiber, newChildren) {
  let newChildIndex = 0; // 新子节点的索引
  // 如果currentFiber有alternate属性，并且alternate有child属性
  let oldFiber = currentFiber.alternate && currentFiber.alternate.child;
  if (oldFiber)
    oldFiber.firstEffect = oldFiber.lastEffect = oldFiber.nextEffect = null;
  let prevSibling; // 上一个新的子fiber
  // 遍历子虚拟DOM 元素数组，为每个虚拟DOM元素创建子Fiber；如果还存在oldFiber，则还要进行删除操作
  while (newChildIndex < newChildren.length || oldFiber) {
    let newChild = newChildren[newChildIndex]; //取出虚拟 DOM 节点
    let newFiber;
    const sameType = oldFiber && newChild && oldFiber.type === newChild.type;
    let tag;
    if (
      newChild &&
      typeof newChild.type === "function" &&
      newChild.type.prototype.isReactComponent
    ) {
      // 处理类组件
      tag = TAG_CLASS;
    } else if (newChild && typeof newChild.type === "function") {
      tag = TAG_FUNCTION_COMPONENT;
    } else if (newChild && newChild.type === ELEMENT_TEXT) {
      tag = TAG_TEXT; //是文本节点
    } else if (newChild && typeof newChild.type === "string") {
      tag = TAG_HOST; // 是原生DOM节点
    }
    // sameType说明老fiber和新虚拟DOM类型一样，可以复用旧的dom节点，只需更新
    if (sameType) {
      if (oldFiber.alternate) {
        newFiber = oldFiber.alternate; // 如果类型一致，并且存在上上次Fiber结构，就可以复用
        newFiber.props = newChild.props;
        newFiber.alternate = oldFiber;
        newFiber.effectTag = UPDATE;
        newFiber.updateQueue =
          (oldFiber && oldFiber.updateQueue) || new UpdateQueue(); // 给类fiber对象使用，已经更新过一次
        newFiber.nextEffect = null;
      } else {
        // 更新Fiber，条件：老Fiber和新虚拟DOM类型一样，可以复用老的DOM节点
        newFiber = {
          tag: oldFiber.tag,
          type: oldFiber.type,
          props: newChild.props,
          stateNode: oldFiber.stateNode,
          return: currentFiber,
          updateQueue: (oldFiber && oldFiber.updateQueue) || new UpdateQueue(),// 给类fiber对象使用
          alternate: oldFiber,
          effectTag: UPDATE,
          nextEffect: null,
        };
      }
    } else {
      if (newChild) {
        // 创建Fiber
        newFiber = {
          tag, //TAG_HOST
          type: newChild.type, // div
          props: newChild.props,
          stateNode: null, // div还没有创建DOM原生
          return: currentFiber, // 父Fiber returnFiber
          effectTag: PLACEMENT, // 副作用标识，render阶段会收集effectlist。收集副作用 添加、删除、更新
          updateQueue: (oldFiber && oldFiber.updateQueue) || new UpdateQueue(),
          nextEffect: null, //effect list是一个单链表
        };
      }
      if (oldFiber) { // 如果类型不一样，仍旧存在oldFiber的话，将oldFiber删除
        oldFiber.effectTag = DELETION;
        deletions.push(oldFiber);
      }
    }
    if (oldFiber) {
      oldFiber = oldFiber.sibling; //旧的是Fiber结构，新的是虚拟dom。oldFiber向后移动指针
    }
    if (newFiber) {
      // newChildIndex说明是子元素中 的第一个节点
      if (newChildIndex === 0) {
        currentFiber.child = newFiber;
      } else {
        // 创建链表关系
        prevSibling.sibling = newFiber;
      }
      // 创建链表关系
      prevSibling = newFiber;
    }
    newChildIndex += 1;
  }
}
/**
 * useReducer:
 * fibering表示: 正在构建的fiber
 * curfiber表示: 已经构建完成的fiber树上的fiber
 * fibering.alternate = curfiber
 */

/**
 * fibering构建时: 会执行useReducer
 * userReduer执行时: 更新curFiber中的curHook
 * 更新后的curHook.sate 就是useReducer返回的state
 * const [state, dispatch] = useReduer(reduer, 0)
 * state 用来生成最新的虚拟dom, 从而生成新的fiber
 */
export function useReducer(reducer, initialValue) {
  // 定义了多次hooks，通过hookIndex进行区分不同的hooks
  // curHook 是 currentFiber树上的值
  let curHook =
    workInProgressFiber.alternate &&
    workInProgressFiber.alternate.hooks &&
    workInProgressFiber.alternate.hooks[hookIndex];
  if (curHook) {
    curHook.state = curHook.updateQueue.forceUpdate(curHook.state);
  } else {
    // 组件第一次渲染走这里
    curHook = {
      state: initialValue,
      updateQueue: new UpdateQueue(),
    };
  }
  /**
   * action 是什么:
   * 当reducer=null的时候, action为: 
   * const [a, setA] = useState(0); 
   * setA(action)
   */

  /**
   * action 是什么:
   * 当reducer存在的时候, action为: 
   * const [a, dispatch] = useReducer(reduer, 0); 
   * const action = {type: 'add'}
   * dispatch(action)
   */
  const dispatch = (action) => {
    curHook.updateQueue.enqueueUpdate(
      // 当reducer=null的时候，就是useState的hooks实现
      new Update(reducer ? reducer(curHook.state, action) : action)
    );
    scheduleRoot();
  };
  workInProgressFiber.hooks[hookIndex++] = curHook;
  return [curHook.state, dispatch];
}
/**
 * curHook是什么:
 * fibering表示正在构建的fiber
 * curFiber = fibering.alternate
 * curHook是curFiber里的hook
 */
/**
 * const [a, setA] = useState(0)
 * 当set(a => a+1)时, callback 会被存到 curHook 中, 然后出发重新渲染
 * 当重新渲染时, curHook.state会被callback更新, curHook.sate会被用于生成新的虚拟dom
 */
export function useState(initialState) {
  return useReducer(null, initialState);
}

requestIdleCallback(workLoop, { timeout: 500 });
